#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "BasicBlock.h"
#include "../List.h"
#include "../6502.h"

BasicBlock** BBRepository;

//Basic Block Instruction
BBInstruction* BBINew(uint16_t Address){
    BBInstruction* BBI;
    
    BBI = malloc(sizeof(BBInstruction));
    
    BBI->Address = Address;
    BBI->AddressingMode = AM_IMPLIED;
    BBI->Opcode = 0xFF;         //JAMS the CPU
    BBI->Operands[0] = BBI->Operands[1] = BBI->Operands[2] = 0;
    BBI->Size = 0;
    
    return BBI;
}

//Basic Block

static int BBInstructionComparator(void* Instruction1, void* Instruction2){
    BBInstruction *BBI1, *BBI2;
    
    BBI1 = (BBInstruction*) Instruction1;
    BBI2 = (BBInstruction*) Instruction2;
    
    return (BBI1->Address- BBI2->Address);
}

static void BBInstructionRemoval(void* Instruction){
    BBInstruction *BBI;
    
    BBI = (BBInstruction*) Instruction;
    
    free(BBI);
}

BasicBlock* BBNew(uint16_t StartingAddress){
    BasicBlock* BB;
    
    BB = malloc(sizeof(BasicBlock));
    
    BB->StartingAddress = StartingAddress;
    BB->NumInstructions = 0;
    BB->Instructions = LST_New(BBInstructionComparator);
    LST_AttachRemovalRoutine(BB->Instructions, BBInstructionRemoval);
    BB->Size = 0;
    
    return BB;
}

void BBInsertInstruction(BasicBlock* BB, BBInstruction* Instruction){
    LST_Enqueue(BB->Instructions, Instruction);
    BB->NumInstructions++;
    BB->Size += CPUGetInstructionSize(Instruction->Opcode);
}

void BBRemoveInstruction(BasicBlock *BB, uint16_t Address){
    BBInstruction Temp, *Removed;
    
    Temp.Address = Address;
    
    if((Removed = LST_Remove(BB->Instructions, &Temp))){
        BB->NumInstructions--;
        BB->Size -= CPUGetInstructionSize(Removed->Opcode);
    }
}

BBInstruction* BBLookupInstruction(BasicBlock* BB, uint16_t Address){
    BBInstruction Temp, *BBI;
    
    Temp.Address = Address;
    
    BBI = LST_Search(BB->Instructions, &Temp);
    
    return BBI;
}

BasicBlock* BBSplit(BasicBlock* BB, uint16_t SplitOn){
    BBInstruction *BBI, *PrevBBI;
    BasicBlock *NewBB = NULL;
    
    BBI = BBLookupInstruction(BB, SplitOn);
    
    if(BBI){
        NewBB = BBNew(SplitOn);
        do{
            BBInsertInstruction(NewBB, BBI);
            PrevBBI = BBI;
            BBI = LST_Next(BB->Instructions);
            BBRemoveInstruction(BB, PrevBBI->Address);
        }while(BBI);
    }
    
    return (NewBB ? NewBB : BB);
}

// Basic Block Repository

void BBRInit(void){
    BBRepository = malloc(sizeof(BasicBlock*) * 0x10000);
    memset(BBRepository, 0, 0x10000 * sizeof(BasicBlock*));
}

void BBRRefresh(BasicBlock* BB){
    uint16_t i, InitialAddress, FinalAddress;
    BBInstruction* BBI;
    
    LST_Rewind(BB->Instructions);
    
    while((BBI = LST_Next(BB->Instructions)))
        BBRepository[BBI->Address] = BB;
    
}

BasicBlock* BBRSearch(uint16_t Address){
    return BBRepository[Address];
}